package com.speljohan.rsbot.script;

import java.awt.Point;

import com.speljohan.rsbot.bot.Bot;
import com.speljohan.rsbot.script.wrappers.RSInterface;
import com.speljohan.rsbot.script.wrappers.RSInterfaceComponent;

/**
 * This class is for all the Bank operations.
 */

public class Bank {
	private final Methods methods;

	Bank(final Methods methods) {
		this.methods = methods;
	}

	/**
	 * Performs a given action on the specified item id. Returns atMenu.
	 * 
	 * @param itemID
	 *            the id of the item
	 * @param txt
	 *            the action to perform (see {@link Methods#atMenu})
	 * @return true on success
	 * */
	public boolean atItem(final int itemID, final String txt) {
		if (!methods.isLoggedIn() || !isOpen()) {
			return false;
		}
		final int[] itemArray = getItemArray();
		for (int off = 0; off < itemArray.length; off++) {
			if (itemArray[off] == itemID) {
				methods.clickMouse(getItemPoint(off), 5, 5, false);
				return methods.atMenu(txt);
			}
		}
		return false;
	}

	/**
	 * Closes the bank interface.
	 * 
	 * @return true if the interface is no longer open
	 * */
	public boolean close() {
		if (!isOpen()) {
			return true;
		}
		methods.atInterface(Constants.INTERFACE_BANK,
				Constants.INTERFACE_BANK_BUTTON_CLOSE);
		methods.wait(methods.random(500, 600));
		return !isOpen();
	}

	/**
	 * Deposits all items in inventory. Returns true on success.
	 * 
	 * @return true on success
	 */
	public boolean depositAll() {
		if (!isOpen()) {
			return false;
		}
		return methods.atInterface(Constants.INTERFACE_BANK,
				Constants.INTERFACE_BANK_BUTTON_DEPOSIT_CARRIED_ITEMS);
	}

	/**
	 * Deposit everything your player has equipped.
	 * 
	 * @return true on success
	 * @since 6 March 2009.
	 */
	public boolean depositAllEquipped() {
		if (!isOpen()) {
			return false;
		}
		return methods.atInterface(Constants.INTERFACE_BANK,
				Constants.INTERFACE_BANK_BUTTON_DEPOSIT_WORN_ITEMS);
	}

	/**
	 * Deposits all items in inventory except for the given ids. Returns true on
	 * success.
	 * 
	 * @param items
	 *            the items not to deposit
	 * @return true on success
	 */
	public boolean depositAllExcept(final int... items) {
		int inventoryCount = methods.getInventoryCount();
		int[] inventoryArray = methods.getInventoryArray();
		outer: for (int off = 0; off < inventoryArray.length; off++) {
			if (inventoryArray[off] == -1) {
				continue;
			}
			for (final int item : items) {
				if (inventoryArray[off] == item) {
					continue outer;
				}
			}

			for (int tries = 0; tries < 5; tries++) {
				methods.atInventoryItem(inventoryArray[off], "Deposit-All");
				methods.wait(methods.random(500, 700));
				if (methods.getInventoryCount() < inventoryCount) {
					break;
				}
			}
			if (methods.getInventoryCount() >= inventoryCount) {
				// equally, otherwise
				// something really weird
				// happend :P
				return false;
			}
			inventoryArray = methods.getInventoryArray();
			inventoryCount = methods.getInventoryCount();
		}
		return true;
	}

	/**
	 * Deposits everything your familiar is carrying.
	 * 
	 * @return true on success
	 * @since 6 March 2009.
	 */
	public boolean depositAllFamiliar() {
		if (!isOpen()) {
			return false;
		}
		return methods.atInterface(Constants.INTERFACE_BANK,
				Constants.INTERFACE_BANK_BUTTON_DEPOSIT_BEAST_INVENTORY);
	}

	/**
	 * Returns the sum of the count of the given items in the bank.
	 * 
	 * @param items
	 *            the array of items
	 * @return the sum of the stacks of the items
	 * */
	public int getCount(final int... items) {
		int itemCount = 0;
		final int[] inventoryArray = getItemArray();
		for (int off = 0; off < inventoryArray.length; off++) {
			for (final int item : items) {
				if (inventoryArray[off] == item) {
					itemCount += getStackSizes()[off];
				}
			}
		}
		return itemCount;
	}

	/**
	 * Gets the bank interface.
	 * 
	 * @return the bank interface
	 * */
	public RSInterface getInterface() {
		return RSInterface.getInterface(Constants.INTERFACE_BANK);
	}

	public RSInterfaceComponent getItem(final int index) {
		final RSInterfaceComponent[] items = getItems();
		if (items != null) {
			for (final RSInterfaceComponent item : items) {
				if (item.getComponentIndex() == index) {
					return item;
				}
			}
		}

		return null;
	}

	/**
	 * Gets the array of item ids in the bank.
	 * 
	 * @return the item array
	 * */
	public int[] getItemArray() {
		final RSInterfaceComponent[] items = getInterface().getChild(
				Constants.INTERFACE_BANK_INVENTORY).getComponents();
		if (items != null) {
			final int[] value = new int[items.length];
			for (int i = 0; i < items.length; i++) {
				value[items[i].getComponentIndex()] = items[i].getComponentID();
			}
			return value;
		}

		return new int[0];
	}

	/**
	 * Makes it easier to get Items in the bank
	 * 
	 * @author Fusion89k
	 * @param id
	 *            ID of the item to get
	 * @return the component of the item
	 */
	public RSInterfaceComponent getItemByID(final int id) {
		final RSInterfaceComponent[] items = getItems();
		if (items != null) {
			for (final RSInterfaceComponent item : items) {
				if (item.getComponentID() == id) {
					return item;
				}
			}
		}

		return null;
	}

	public String[] getItemNames() {
		final RSInterfaceComponent[] items = getInterface().getChild(
				Constants.INTERFACE_BANK_INVENTORY).getComponents();
		if (items != null) {
			final String[] value = new String[items.length];
			for (int i = 0; i < items.length; i++) {
				value[items[i].getComponentIndex()] = items[i]
				                                            .getComponentName();
			}
			return value;
		}

		return new String[0];
	}

	/**
	 * Gets the point on the screen for a given item.
	 * 
	 * Numbered left to right then top to bottom.
	 * 
	 * @author Qauters
	 * @param slot
	 *            the index of the item
	 * @return the point of the item
	 * */
	public Point getItemPoint(final int slot) {
		// And I will strike down upon thee with great vengeance and furious
		// anger
		// those who attempt to replace the following code with fixed constants!

		if (slot < 0) {
			throw new IllegalArgumentException("slot < 0 " + slot);
		}

		final RSInterfaceComponent item = getItem(slot);
		if (item != null) {
			return item.getPosition();
		}

		return new Point(-1, -1);
	}

	public RSInterfaceComponent[] getItems() {
		if (getInterface() == null
				|| getInterface().getChild(Constants.INTERFACE_BANK_INVENTORY) == null) {
			return new RSInterfaceComponent[0];
		}

		return getInterface().getChild(Constants.INTERFACE_BANK_INVENTORY)
		.getComponents();
	}

	/**
	 * Gets the array of item stack sizes in the bank.
	 * 
	 * @return the stack sizes
	 * */
	public int[] getStackSizes() {
		final RSInterfaceComponent[] items = getInterface().getChild(
				Constants.INTERFACE_BANK_INVENTORY).getComponents();
		if (items != null) {
			final int[] value = new int[items.length];
			for (int i = 0; i < items.length; i++) {
				value[items[i].getComponentIndex()] = items[i]
				                                            .getComponentStackSize();
			}
			return value;
		}

		return new int[0];
	}

	/**
	 * @return true if the bank interface is open, false otherwise
	 */
	public boolean isOpen() {
		return getInterface().isValid();
	}

	/**
	 * Opens the bank tab.
	 * 
	 * @param tabNumber
	 *            the tab number -- e.g. view all is 1.
	 * @return true on success
	 * @since 6 March 2009.
	 */
	public boolean openTab(final int tabNumber) {
		if (!isOpen()) {
			return false;
		}
		return methods.atInterface(Constants.INTERFACE_BANK,
				Constants.INTERFACE_BANK_TAB[tabNumber - 1]);
	}

	/**
	 * Searches for an item in the bank. Returns true if succeeded (does not
	 * necessarily means found)
	 * 
	 * @param itemName
	 *            The item name to find
	 * @return true on success
	 * */
	public boolean searchItem(final String itemName) {
		if (!isOpen()) {
			return false;
		}

		methods.atInterface(Constants.INTERFACE_BANK,
				Constants.INTERFACE_BANK_BUTTON_SEARCH, "Search");
		methods.wait(methods.random(1000, 2000));

		if (isOpen()
				&& RSInterface.getInterface(Constants.INTERFACE_BANK_SEARCH)
				.isValid()) {
			Bot.getInputManager().sendKeys(itemName, false);
			methods.wait(methods.random(300, 700));
			return true;
		}
		return false;
	}

	/**
	 * Sets the bank rearrange mode to insert
	 * 
	 * @return true on success
	 * */
	public boolean setRearrangeModeToInsert() {
		if (!isOpen()) {
			return false;
		}
		if (methods.getSetting(Constants.SETTING_BANK_TOGGLE_REARRANGE_MODE) != 1) {
			methods.atInterface(Constants.INTERFACE_BANK,
					Constants.INTERFACE_BANK_BUTTON_INSERT);
			methods.wait(methods.random(500, 700));
		}
		return methods.getSetting(Constants.SETTING_BANK_TOGGLE_REARRANGE_MODE) == 1;
	}

	/**
	 * Sets the bank rearrange mode to swap
	 * 
	 * @return true on success
	 * */
	public boolean setRearrangeModeToSwap() {
		if (!isOpen()) {
			return false;
		}
		if (methods.getSetting(Constants.SETTING_BANK_TOGGLE_REARRANGE_MODE) != 0) {
			methods.atInterface(Constants.INTERFACE_BANK,
					Constants.INTERFACE_BANK_BUTTON_SWAP);
			methods.wait(methods.random(500, 700));
		}
		return methods.getSetting(Constants.SETTING_BANK_TOGGLE_REARRANGE_MODE) == 0;
	}

	/**
	 * Sets the bank withdraw mode to item
	 * 
	 * @return true on success
	 * */
	public boolean setWithdrawModeToItem() {
		if (!isOpen()) {
			return false;
		}
		if (methods.getSetting(Constants.SETTING_BANK_TOGGLE_WITHDRAW_MODE) != 0) {
			methods.atInterface(Constants.INTERFACE_BANK,
					Constants.INTERFACE_BANK_BUTTON_ITEM);
			methods.wait(methods.random(500, 700));
		}
		return methods.getSetting(Constants.SETTING_BANK_TOGGLE_WITHDRAW_MODE) == 0;
	}

	/**
	 * Sets the bank withdraw mode to note
	 * 
	 * @return true on success
	 * */
	public boolean setWithdrawModeToNote() {
		if (!isOpen()) {
			return false;
		}
		if (methods.getSetting(Constants.SETTING_BANK_TOGGLE_WITHDRAW_MODE) != 1) {
			methods.atInterface(Constants.INTERFACE_BANK,
					Constants.INTERFACE_BANK_BUTTON_NOTE);
			methods.wait(methods.random(500, 700));
		}
		return methods.getSetting(Constants.SETTING_BANK_TOGGLE_WITHDRAW_MODE) == 1;
	}

	/**
	 * Tries to withdraw an item.
	 * 
	 * 0 is All. 1,5,10 use Withdraw 1,5,10 while other numbers Withdraw X.
	 * 
	 * @param itemID
	 *            The id of the item.
	 * @param count
	 *            The number to withdraw.
	 * @return true on success
	 * */
	public boolean withdraw(final int itemID, final int count) {
		if (count < 0) {
			throw new IllegalArgumentException("count < 0 " + count);
		}
		if (!isOpen()) {
			return false;
		}
		final int inventoryCount = methods.getInventoryCount(true);
		for (int tries = 0; tries < 5; tries++) {
			switch (count) {
			case 0: // Withdraw All
				atItem(itemID, "Withdraw-All");
				break;
			case 1: // Withdraw 1
				atItem(itemID, "Withdraw-1");
				break;
			case 5: // Withdraw 5
				atItem(itemID, "Withdraw-5");
				break;
			case 10: // Withdraw 10
				atItem(itemID, "Withdraw-10");
				break;
			case 50: // Withdraw 50
				atItem(itemID, "Withdraw-50");
			default: // Withdraw x
				atItem(itemID, "Withdraw-X");
			methods.wait(methods.random(900, 1100));
			Bot.getInputManager().sendKeys("" + count, true);
			}
			methods.wait(methods.random(500, 700));
			if (methods.getInventoryCount(true) > inventoryCount) {
				return true;
			}
		}
		return false;
	}
}
